package libpcapmain

import (
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"goproduct/config"
	"goproduct/util"
	"log"
	"net"
	"strconv"
	"strings"
	"syscall"
	"time"
)

/**
base
*/
var handleReceive *pcap.Handle
var err error
var blockIp string
var whiteIp string
var fds int
var deviceIdReceive string
var deviceIdSend string
var addr = new(syscall.SockaddrInet4)

//Packet buffer, SerializeLayers Clears the given write buffer
var buffer = gopacket.NewSerializeBuffer()
var Payload = gopacket.Payload("kb_iot")

func init() {
	//1.0 Reading a configuration file
	parser := config.InitConfig()
	fds, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
	if err != nil {
		panic(err)
	}
	//2.0 Gets the network card that receives the data and generates the active handle
	deviceIdReceive = parser.GetString("common", "deviceIdReceive")
	handleReceive, err = pcap.OpenLive(
		deviceIdReceive, // network device
		int32(65535),
		true,
		time.Microsecond,
	)
	if err != nil {
		fmt.Println("Mirror Handler error", err.Error())
		return
	}
	//3.0
	deviceIdSend = util.GetDeviceId()
	err = syscall.BindToDevice(fds, deviceIdSend)
	if err != nil {
		panic(err)
	}
	//4.0 设置阻断IP
	blockIp = parser.GetString("block", "black_list")
	//5.0 设置no block IP,单个
	whiteIp = parser.GetString("block", "white_list")
	//3.0 获取第三个参数,设置BPF
	bpfFilter := parser.GetString("block", "bpfFilter")
	err = handleReceive.SetBPFFilter(bpfFilter)
	if err != nil {
		panic(err)
	}
	fmt.Println("The program has been started")
	fmt.Println("当前阻断IP:", blockIp)
	fmt.Println("当前white IP:", whiteIp)
	fmt.Println("当前BPF:", bpfFilter)
	fmt.Println("当前deviceIdReceive:", deviceIdReceive)
	fmt.Println("当前deviceIdSend:", deviceIdSend)
}
func Main() {
	var eth layers.Ethernet
	var dot1q layers.Dot1Q
	var ipv4 layers.IPv4
	var tcp layers.TCP

	dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil))
	dlc = dlc.Put(&eth)
	dlc = dlc.Put(&dot1q)
	dlc = dlc.Put(&ipv4)
	dlc = dlc.Put(&tcp)
	parserD := dlc.LayersDecoder(layers.LayerTypeEthernet, gopacket.NilDecodeFeedback)
	decoded := make([]gopacket.LayerType, 0, 20)
	defer syscall.Close(fds)
	defer handleReceive.Close()
	for true {
		//Generate the array one byte at a time,Returns a pointer to a byte array,
		//Each call to ZeroCopyReadPacketData invalidates any data previously returned by ZeroCopyReadPacketData
		data, _, _ := handleReceive.ZeroCopyReadPacketData()
		//Parse the IP and TCP information in the packet
		lt, err := parserD(data, &decoded)
		if err != nil {
			continue
		}
		if lt != gopacket.LayerTypeZero {
			continue
		}
		//Incomplete packets
		if ipv4.SrcIP == nil || ipv4.DstIP == nil {
			continue
		}
		//Whitelist selection
		if util.GetIpsMaskContent(whiteIp, ipv4.SrcIP.String()) || util.GetIpsMaskContent(whiteIp, ipv4.DstIP.String()) {
			continue
		}
		//Blacklist filtering
		if !util.GetIpsMaskContent(blockIp, ipv4.SrcIP.String()) && !util.GetIpsMaskContent(blockIp, ipv4.DstIP.String()) {
			continue
		}
		sendPacket(ipv4, tcp, false)
	}
}

func sendPacket(ipv4 layers.IPv4, tcp layers.TCP, redirect bool) {
	//握手完成开始推送数据时候拦截
	if redirect {
		//1.0 拦截第一次握手
		if tcp.SYN && !tcp.ACK {
			//伪造第二次握手, seq = 随机值。Ack_seq = old_seq + 1
			SendSYNACK(ipv4, tcp)
		}
		if tcp.ACK && tcp.PSH {
			//构造重定向包,seq= old_ack_seq; Ack_seq = old_seq + data_len
			SendRedirect(ipv4.DstIP, ipv4.SrcIP, tcp.DstPort, tcp.SrcPort, tcp.Seq, tcp.Ack)
		}
	} else {
		if tcp.SYN || tcp.FIN || tcp.RST {
			//log.Println("send nothing")
			return
		}
		//阻断主要是发送的SEQ序列号和上一位的ACK值相等
		SendRST(ipv4, tcp)
	}
}

func SendRST(ipv4 layers.IPv4, tcp layers.TCP) {
	ipv4c := layers.IPv4{
		SrcIP:      ipv4.DstIP,
		DstIP:      ipv4.SrcIP,
		Protocol:   layers.IPProtocolTCP,
		Id:         10000,
		Version:    4, //must
		IHL:        5,
		TOS:        0,
		TTL:        64,
		Checksum:   0,
		FragOffset: 0,
	}
	tcpc := layers.TCP{
		SrcPort: tcp.DstPort,
		DstPort: tcp.SrcPort,
		RST:     true,
		PSH:     true,
		Seq:     tcp.Ack,
		Ack:     10000,
	}
	if err = tcpc.SetNetworkLayerForChecksum(&ipv4c); err != nil {
		log.Fatalln(err)
		return
	}
	err = gopacket.SerializeLayers(
		buffer,
		gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true},
		&ipv4c,
		&tcpc,
		Payload)
	if err != nil {
		log.Fatalln(err)
		return
	}
	addr.Addr = InetAton(ipv4c.DstIP)
	addr.Port = int(tcpc.DstPort)
	err = syscall.Sendto(fds, buffer.Bytes(), 0, addr)
	if err != nil {
		log.Fatalln(err)
		return
	}
}

func SendSYNACK(ipv4 layers.IPv4, tcp layers.TCP) {
	ipv4c := layers.IPv4{
		SrcIP:      ipv4.DstIP,
		DstIP:      ipv4.SrcIP,
		Protocol:   layers.IPProtocolTCP,
		Id:         10000,
		Version:    4, //must
		IHL:        5,
		TOS:        0,
		TTL:        64,
		Checksum:   0,
		FragOffset: 0,
	}
	tcpc := layers.TCP{
		SrcPort: tcp.DstPort,
		DstPort: tcp.SrcPort,
		SYN:     true,
		ACK:     true,
		Seq:     10000,
		Ack:     tcp.Seq + 1,
	}
	if err = tcpc.SetNetworkLayerForChecksum(&ipv4c); err != nil {
		log.Fatalln(err)
		return
	}
	err = gopacket.SerializeLayers(
		buffer,
		gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true},
		&ipv4c,
		&tcpc,
		Payload)
	if err != nil {
		log.Fatalln(err)
		return
	}
	addr.Addr = InetAton(ipv4c.DstIP)
	addr.Port = int(tcpc.DstPort)
	err = syscall.Sendto(fds, buffer.Bytes(), 0, addr)
	if err != nil {
		log.Fatalln(err)
		return
	}
}

func SendRedirect(srcIp net.IP, dstIp net.IP, srcPort layers.TCPPort, dstPort layers.TCPPort, seq uint32, ack uint32) {
	ipv4 := layers.IPv4{
		SrcIP:      srcIp,
		DstIP:      dstIp,
		Protocol:   layers.IPProtocolTCP,
		Id:         10000,
		Version:    4, //must
		IHL:        5,
		TOS:        0,
		TTL:        64,
		Checksum:   0,
		FragOffset: 0,
	}
	tcp := layers.TCP{
		SrcPort: srcPort,
		DstPort: dstPort,
		FIN:     true,
		ACK:     true,
		PSH:     false,
		Seq:     seq,
		Ack:     seq,
	}

	if err = tcp.SetNetworkLayerForChecksum(&ipv4); err != nil {
		log.Fatalln(err)
		return
	}

	err = gopacket.SerializeLayers(
		buffer,
		gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true},
		&ipv4,
		&tcp,
		gopacket.Payload("HTTP/1.1 302 Temporary Redirect\\r\\nLocation: https://180.101.49.12/?\\r\\n\\r\\n"))
	if err != nil {
		log.Fatalln(err)
		return
	}
	addr.Addr = InetAton(ipv4.DstIP)
	addr.Port = int(tcp.DstPort)
	err = syscall.Sendto(fds, buffer.Bytes(), 0, addr)
	if err != nil {
		log.Fatalln(err)
		return
	}
}

func InetAton(ip net.IP) [4]byte {
	bits := strings.Split(ip.String(), ".")
	ip.MarshalText()
	b0, _ := strconv.Atoi(bits[0])
	b1, _ := strconv.Atoi(bits[1])
	b2, _ := strconv.Atoi(bits[2])
	b3, _ := strconv.Atoi(bits[3])
	return [4]byte{byte(b0), byte(b1), byte(b2), byte(b3)}
}
